/*
 * @Author: lixu lixu@puchigames.com
 * @Date: 2025-09-18 11:35:00
 * @LastEditors: lixu lixu@puchigames.com
 * @LastEditTime: 2025-09-18 11:35:00
 * @FilePath: /go-helper/ixLog/hooks.go
 * @Description: 日志钩子实现，支持各种日志事件的自定义处理
 */

package ixLog

import (
	"encoding/json"
	"fmt"
	"net/http"
	"net/smtp"
	"os"
	"strings"
	"sync"
	"time"
)

// EmailHook 邮件钩子
type EmailHook struct {
	host     string
	port     string
	username string
	password string
	from     string
	to       []string
	subject  string
	levels   []LogLevel
	mutex    sync.Mutex
}

// NewEmailHook 创建邮件钩子
func NewEmailHook(host, port, username, password, from string, to []string, subject string, levels []LogLevel) *EmailHook {
	if subject == "" {
		subject = "应用日志告警"
	}

	return &EmailHook{
		host:     host,
		port:     port,
		username: username,
		password: password,
		from:     from,
		to:       to,
		subject:  subject,
		levels:   levels,
	}
}

// Fire 触发钩子
func (h *EmailHook) Fire(entry *LogEntry) error {
	h.mutex.Lock()
	defer h.mutex.Unlock()

	// 构建邮件内容
	body := fmt.Sprintf(`
时间: %s
级别: %s
消息: %s
主机: %s
应用: %s
`,
		entry.Timestamp.Format("2006-01-02 15:04:05"),
		entry.Level.String(),
		entry.Message,
		entry.Fields["host"],
		entry.Fields["app"],
	)

	if entry.TraceID != "" {
		body += fmt.Sprintf("追踪ID: %s\n", entry.TraceID)
	}

	if entry.Error != nil {
		body += fmt.Sprintf("错误: %s\n", entry.Error.Error())
	}

	if len(entry.Stack) > 0 {
		body += "调用栈:\n"
		for _, frame := range entry.Stack {
			body += fmt.Sprintf("  %s\n", frame)
		}
	}

	// 发送邮件
	return h.sendEmail(h.subject, body)
}

// Levels 返回关注的日志级别
func (h *EmailHook) Levels() []LogLevel {
	return h.levels
}

// sendEmail 发送邮件
func (h *EmailHook) sendEmail(subject, body string) error {
	auth := smtp.PlainAuth("", h.username, h.password, h.host)

	msg := fmt.Sprintf("To: %s\r\nSubject: %s\r\n\r\n%s",
		strings.Join(h.to, ","), subject, body)

	addr := h.host + ":" + h.port
	return smtp.SendMail(addr, auth, h.from, h.to, []byte(msg))
}

// WebhookHook Webhook钩子
type WebhookHook struct {
	url     string
	method  string
	headers map[string]string
	levels  []LogLevel
	client  *http.Client
	mutex   sync.Mutex
}

// NewWebhookHook 创建Webhook钩子
func NewWebhookHook(webhookURL, method string, headers map[string]string, levels []LogLevel) *WebhookHook {
	if method == "" {
		method = "POST"
	}

	return &WebhookHook{
		url:     webhookURL,
		method:  method,
		headers: headers,
		levels:  levels,
		client: &http.Client{
			Timeout: 10 * time.Second,
		},
	}
}

// Fire 触发钩子
func (h *WebhookHook) Fire(entry *LogEntry) error {
	h.mutex.Lock()
	defer h.mutex.Unlock()

	// 构建请求数据
	data := map[string]interface{}{
		"timestamp": entry.Timestamp.Format(time.RFC3339),
		"level":     entry.Level.String(),
		"message":   entry.Message,
		"fields":    entry.Fields,
	}

	if entry.TraceID != "" {
		data["trace_id"] = entry.TraceID
	}

	if entry.Error != nil {
		data["error"] = entry.Error.Error()
	}

	// 序列化数据
	jsonData, err := json.Marshal(data)
	if err != nil {
		return fmt.Errorf("序列化数据失败: %w", err)
	}

	// 创建请求
	req, err := http.NewRequest(h.method, h.url, strings.NewReader(string(jsonData)))
	if err != nil {
		return fmt.Errorf("创建请求失败: %w", err)
	}

	// 设置请求头
	req.Header.Set("Content-Type", "application/json")
	for key, value := range h.headers {
		req.Header.Set(key, value)
	}

	// 发送请求
	resp, err := h.client.Do(req)
	if err != nil {
		return fmt.Errorf("发送请求失败: %w", err)
	}
	defer resp.Body.Close()

	if resp.StatusCode < 200 || resp.StatusCode >= 300 {
		return fmt.Errorf("Webhook响应错误: %d", resp.StatusCode)
	}

	return nil
}

// Levels 返回关注的日志级别
func (h *WebhookHook) Levels() []LogLevel {
	return h.levels
}

// FileHook 文件钩子（用于将特定级别的日志写入单独文件）
type FileHook struct {
	filePath string
	levels   []LogLevel
	writer   Writer
	mutex    sync.Mutex
}

// NewFileHook 创建文件钩子
func NewFileHook(filePath string, levels []LogLevel) (*FileHook, error) {
	config := &OutputConfig{
		Type:     FileTarget,
		Enable:   true,
		FilePath: filePath,
		Rotation: SizeRotation,
		MaxSize:  50, // 50MB
		MaxAge:   7,  // 7天
		Compress: true,
	}

	writer, err := CreateWriter(config)
	if err != nil {
		return nil, fmt.Errorf("创建文件写入器失败: %w", err)
	}

	return &FileHook{
		filePath: filePath,
		levels:   levels,
		writer:   writer,
	}, nil
}

// Fire 触发钩子
func (h *FileHook) Fire(entry *LogEntry) error {
	h.mutex.Lock()
	defer h.mutex.Unlock()

	// 使用简单的文本格式
	formatter := NewTextFormatter(false, DefaultTimeFormat, true, "")
	data, err := formatter.Format(entry)
	if err != nil {
		return fmt.Errorf("格式化失败: %w", err)
	}

	_, err = h.writer.Write(data)
	return err
}

// Levels 返回关注的日志级别
func (h *FileHook) Levels() []LogLevel {
	return h.levels
}

// Close 关闭钩子
func (h *FileHook) Close() error {
	return h.writer.Close()
}

// SlackHook Slack钩子
type SlackHook struct {
	webhookURL string
	channel    string
	username   string
	levels     []LogLevel
	client     *http.Client
	mutex      sync.Mutex
}

// NewSlackHook 创建Slack钩子
func NewSlackHook(webhookURL, channel, username string, levels []LogLevel) *SlackHook {
	if username == "" {
		username = "LogBot"
	}

	return &SlackHook{
		webhookURL: webhookURL,
		channel:    channel,
		username:   username,
		levels:     levels,
		client: &http.Client{
			Timeout: 10 * time.Second,
		},
	}
}

// Fire 触发钩子
func (h *SlackHook) Fire(entry *LogEntry) error {
	h.mutex.Lock()
	defer h.mutex.Unlock()

	// 构建Slack消息
	color := h.getLevelColor(entry.Level)
	text := fmt.Sprintf("*%s* | %s", entry.Level.String(), entry.Message)

	if entry.Error != nil {
		text += fmt.Sprintf("\n错误: `%s`", entry.Error.Error())
	}

	if entry.TraceID != "" {
		text += fmt.Sprintf("\n追踪ID: `%s`", entry.TraceID)
	}

	payload := map[string]interface{}{
		"channel":  h.channel,
		"username": h.username,
		"attachments": []map[string]interface{}{
			{
				"color":     color,
				"text":      text,
				"timestamp": entry.Timestamp.Unix(),
				"fields": []map[string]interface{}{
					{
						"title": "应用",
						"value": entry.Fields["app"],
						"short": true,
					},
					{
						"title": "主机",
						"value": entry.Fields["host"],
						"short": true,
					},
				},
			},
		},
	}

	// 序列化并发送
	jsonData, err := json.Marshal(payload)
	if err != nil {
		return fmt.Errorf("序列化Slack消息失败: %w", err)
	}

	resp, err := h.client.Post(h.webhookURL, "application/json", strings.NewReader(string(jsonData)))
	if err != nil {
		return fmt.Errorf("发送Slack消息失败: %w", err)
	}
	defer resp.Body.Close()

	if resp.StatusCode != http.StatusOK {
		return fmt.Errorf("Slack响应错误: %d", resp.StatusCode)
	}

	return nil
}

// getLevelColor 获取级别对应的颜色
func (h *SlackHook) getLevelColor(level LogLevel) string {
	switch level {
	case TRACE:
		return "#808080" // 灰色
	case DEBUG:
		return "#36a64f" // 绿色
	case INFO:
		return "#2196f3" // 蓝色
	case WARN:
		return "#ff9800" // 橙色
	case ERROR:
		return "#f44336" // 红色
	case FATAL:
		return "#9c27b0" // 紫色
	default:
		return "#000000" // 黑色
	}
}

// Levels 返回关注的日志级别
func (h *SlackHook) Levels() []LogLevel {
	return h.levels
}

// FunctionHook 函数钩子
type FunctionHook struct {
	fn     func(*LogEntry) error
	levels []LogLevel
}

// NewFunctionHook 创建函数钩子
func NewFunctionHook(fn func(*LogEntry) error, levels []LogLevel) *FunctionHook {
	return &FunctionHook{
		fn:     fn,
		levels: levels,
	}
}

// Fire 触发钩子
func (h *FunctionHook) Fire(entry *LogEntry) error {
	return h.fn(entry)
}

// Levels 返回关注的日志级别
func (h *FunctionHook) Levels() []LogLevel {
	return h.levels
}

// RateLimitHook 限流钩子
type RateLimitHook struct {
	hook     Hook
	rate     int       // 每秒允许的次数
	burst    int       // 突发容量
	tokens   int       // 当前令牌数
	lastTime time.Time // 上次更新时间
	mutex    sync.Mutex
}

// NewRateLimitHook 创建限流钩子
func NewRateLimitHook(hook Hook, rate, burst int) *RateLimitHook {
	return &RateLimitHook{
		hook:     hook,
		rate:     rate,
		burst:    burst,
		tokens:   burst,
		lastTime: time.Now(),
	}
}

// Fire 触发钩子
func (h *RateLimitHook) Fire(entry *LogEntry) error {
	h.mutex.Lock()
	defer h.mutex.Unlock()

	now := time.Now()
	elapsed := now.Sub(h.lastTime)
	h.lastTime = now

	// 添加令牌
	tokensToAdd := int(elapsed.Seconds()) * h.rate
	h.tokens += tokensToAdd
	if h.tokens > h.burst {
		h.tokens = h.burst
	}

	// 检查是否有令牌
	if h.tokens <= 0 {
		return nil // 限流，不执行钩子
	}

	h.tokens--
	return h.hook.Fire(entry)
}

// Levels 返回关注的日志级别
func (h *RateLimitHook) Levels() []LogLevel {
	return h.hook.Levels()
}

// AsyncHook 异步钩子
type AsyncHook struct {
	hook   Hook
	buffer chan *LogEntry
	done   chan struct{}
	wg     sync.WaitGroup
}

// NewAsyncHook 创建异步钩子
func NewAsyncHook(hook Hook, bufferSize int) *AsyncHook {
	ah := &AsyncHook{
		hook:   hook,
		buffer: make(chan *LogEntry, bufferSize),
		done:   make(chan struct{}),
	}

	ah.wg.Add(1)
	go ah.processLoop()

	return ah
}

// Fire 触发钩子
func (h *AsyncHook) Fire(entry *LogEntry) error {
	// 复制entry避免竞态条件
	entryCopy := *entry
	if entry.Fields != nil {
		entryCopy.Fields = make(map[string]interface{})
		for k, v := range entry.Fields {
			entryCopy.Fields[k] = v
		}
	}

	select {
	case h.buffer <- &entryCopy:
		return nil
	default:
		// 缓冲区满，丢弃日志
		return nil
	}
}

// Levels 返回关注的日志级别
func (h *AsyncHook) Levels() []LogLevel {
	return h.hook.Levels()
}

// Close 关闭异步钩子
func (h *AsyncHook) Close() {
	close(h.done)
	h.wg.Wait()
}

// processLoop 处理循环
func (h *AsyncHook) processLoop() {
	defer h.wg.Done()

	for {
		select {
		case entry := <-h.buffer:
			h.hook.Fire(entry)
		case <-h.done:
			// 处理剩余日志
			for len(h.buffer) > 0 {
				entry := <-h.buffer
				h.hook.Fire(entry)
			}
			return
		}
	}
}

// MetricsHook 指标钩子
type MetricsHook struct {
	counters map[LogLevel]int64
	mutex    sync.RWMutex
}

// NewMetricsHook 创建指标钩子
func NewMetricsHook() *MetricsHook {
	return &MetricsHook{
		counters: make(map[LogLevel]int64),
	}
}

// Fire 触发钩子
func (h *MetricsHook) Fire(entry *LogEntry) error {
	h.mutex.Lock()
	defer h.mutex.Unlock()

	h.counters[entry.Level]++
	return nil
}

// Levels 返回关注的日志级别
func (h *MetricsHook) Levels() []LogLevel {
	return []LogLevel{TRACE, DEBUG, INFO, WARN, ERROR, FATAL}
}

// GetMetrics 获取指标
func (h *MetricsHook) GetMetrics() map[LogLevel]int64 {
	h.mutex.RLock()
	defer h.mutex.RUnlock()

	metrics := make(map[LogLevel]int64)
	for level, count := range h.counters {
		metrics[level] = count
	}

	return metrics
}

// Reset 重置指标
func (h *MetricsHook) Reset() {
	h.mutex.Lock()
	defer h.mutex.Unlock()

	for level := range h.counters {
		h.counters[level] = 0
	}
}

// ConsoleHook 控制台钩子（用于将特定级别日志输出到stderr）
type ConsoleHook struct {
	levels []LogLevel
	writer Writer
}

// NewConsoleHook 创建控制台钩子
func NewConsoleHook(levels []LogLevel, useStderr bool) *ConsoleHook {
	var writer Writer
	if useStderr {
		writer = NewConsoleWriter(os.Stderr)
	} else {
		writer = NewConsoleWriter(os.Stdout)
	}

	return &ConsoleHook{
		levels: levels,
		writer: writer,
	}
}

// Fire 触发钩子
func (h *ConsoleHook) Fire(entry *LogEntry) error {
	formatter := NewTextFormatter(true, DefaultTimeFormat, true, "")
	data, err := formatter.Format(entry)
	if err != nil {
		return err
	}

	_, err = h.writer.Write(data)
	return err
}

// Levels 返回关注的日志级别
func (h *ConsoleHook) Levels() []LogLevel {
	return h.levels
}

// FilterHook 过滤钩子
type FilterHook struct {
	hook   Hook
	filter func(*LogEntry) bool
}

// NewFilterHook 创建过滤钩子
func NewFilterHook(hook Hook, filter func(*LogEntry) bool) *FilterHook {
	return &FilterHook{
		hook:   hook,
		filter: filter,
	}
}

// Fire 触发钩子
func (h *FilterHook) Fire(entry *LogEntry) error {
	if h.filter(entry) {
		return h.hook.Fire(entry)
	}
	return nil
}

// Levels 返回关注的日志级别
func (h *FilterHook) Levels() []LogLevel {
	return h.hook.Levels()
}

// 预定义钩子创建函数

// ErrorAlertHook 创建错误告警钩子
func ErrorAlertHook(webhookURL string) Hook {
	return NewWebhookHook(webhookURL, "POST", nil, []LogLevel{ERROR, FATAL})
}

// DebugFileHook 创建调试文件钩子
func DebugFileHook(filePath string) (Hook, error) {
	return NewFileHook(filePath, []LogLevel{DEBUG, TRACE})
}

// ErrorFileHook 创建错误文件钩子
func ErrorFileHook(filePath string) (Hook, error) {
	return NewFileHook(filePath, []LogLevel{ERROR, FATAL})
}

// TraceFilterHook 创建追踪过滤钩子
func TraceFilterHook(hook Hook, traceIDs []string) Hook {
	traceMap := make(map[string]bool)
	for _, id := range traceIDs {
		traceMap[id] = true
	}

	return NewFilterHook(hook, func(entry *LogEntry) bool {
		return traceMap[entry.TraceID]
	})
}
